﻿using NPOI;
using NPOI.HSSF.UserModel;
using NPOI.OpenXml4Net.OPC;
using NPOI.POIFS.FileSystem;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace TYBase
{
    public class ExcelHelper
    {
        public IWorkbook CreateWorkbook(string t)
        {
            IWorkbook workbook = null;
            if (t == ".xlsx") // 2007版本
                workbook = new XSSFWorkbook();
            else if (t == ".xls") // 2003版本
                workbook = new HSSFWorkbook();
            return workbook;
        }

        private static IWorkbook NPOIOpenExcel(string filename)
        {
            if (!File.Exists(filename))
            {
                if (filename.ToLower().IndexOf(".xlsx") > 0) // 2007版本
                    return new XSSFWorkbook();
                else if (filename.ToLower().IndexOf(".xls") > 0) // 2003版本
                    return new HSSFWorkbook();
            }
            Stream excelStream = OpenResource(filename);
            if (POIFSFileSystem.HasPOIFSHeader(excelStream))
                return new HSSFWorkbook(excelStream);
            if (POIXMLDocument.HasOOXMLHeader(excelStream))
            {
                return new XSSFWorkbook(OPCPackage.Open(excelStream));
            }
            if (filename.EndsWith(".xlsx"))
            {
                return new XSSFWorkbook(excelStream);
            }
            if (filename.EndsWith(".xls"))
            {
                new HSSFWorkbook(excelStream);
            }
            return null;
        }

        private static Stream OpenResource(string filename)
        {
            FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
            return fs;
        }

        public static bool AppendData(string fileName, List<List<string>> listData, bool addIndex = false)
        {
            int i = 0;
            int j = 0;
            int count = 0;
            ISheet sheet = null;
            IWorkbook workbook = null;

            try
            {
                workbook = NPOIOpenExcel(fileName);
                if (workbook != null)
                {
                    if (workbook.NumberOfSheets == 0)
                        sheet = workbook.CreateSheet("Sheet1");
                    else
                    {
                        sheet = workbook.GetSheetAt(0);
                    }
                }
                else
                {
                    Log.I("excel 存入失败 workbook null!");
                    return false;
                }
                if (sheet == null)
                    return false;
                int lastRowNum = sheet.LastRowNum;
                if (lastRowNum == 0)
                    lastRowNum = -1;

                int startIndex = addIndex == true ? 1 : 0;
                for (i = 0; i < listData.Count; ++i)
                {
                    IRow row = sheet.CreateRow(lastRowNum + i + 1);
                    if (addIndex)
                    {
                        if (lastRowNum == -1 && i == 0)
                        {
                            row.CreateCell(0).SetCellValue("序号");
                        }
                        else
                        {
                            row.CreateCell(0).SetCellValue(lastRowNum + i + 1);
                        }
                    }

                    for (j = 0; j < listData[i].Count; ++j)
                    {
                        row.CreateCell(startIndex + j).SetCellValue(listData[i][j]);
                    }
                    ++count;
                }
                using (FileStream fout = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite))//写入流
                {
                    fout.Flush();
                    workbook.Write(fout);//写入文件
                }

                sheet = null;
                workbook = null;
                return true;
            }
            catch (Exception ex)
            {
                Log.E(ex, "excel 追加失败!");
            }
            return false;
        }
        private static Dictionary<string, StreamWriter> dicsw = new Dictionary<string, StreamWriter>();

        private static object lockerSw = new object();
        private static void AddCacheSw(string fileName, StreamWriter sw) {
            lock (lockerSw) {
                dicsw[fileName] = sw;
            }
        }
        private static StreamWriter GetCacheSw(string fileName) {
            lock (lockerSw) {
                if (dicsw.ContainsKey(fileName)) {
                    return dicsw[fileName];
                }
                return null;

            }
        }
        private static object lockRW = new object();
        public static bool AppendToTXTByCache(string fileName, List<List<string>> listData) {
            try {
                var sw = GetCacheSw(fileName);
                if (sw == null) {
                    StreamWriter swTmp = new StreamWriter(fileName, true, Encoding.UTF8, 65536);
                    sw = swTmp;
                    AddCacheSw(fileName, swTmp);
                }
                lock (lockRW) {
                    StringBuilder content = new StringBuilder();
                    for (int i = 0; i < listData.Count; i++) {
                        string strWrite = string.Join("\t", listData[i]);
                        content.AppendLine(strWrite);
                    }
                    sw.Write(content.ToString());
                    sw.Flush(); // 每次写入后刷新缓冲区，确保内容被写入文件
                }
                return true;
            }
            catch (Exception ex) {
                Console.WriteLine(ex);
            }
            return false;
        }

        public static bool AppendToTXTByCache(string fileName, string data) {
            try {
                var sw = GetCacheSw(fileName);
                if (sw == null) {
                    StreamWriter swTmp = new StreamWriter(fileName, true, Encoding.UTF8, 65536);
                    sw = swTmp;
                    AddCacheSw(fileName, swTmp);
                }
                lock (lockRW) {
                    StringBuilder content = new StringBuilder();
                    content.AppendLine(data);
                    sw.Write(content.ToString());
                    sw.Flush(); // 每次写入后刷新缓冲区，确保内容被写入文件
                }
                return true;
            }
            catch (Exception ex) {
                Console.WriteLine(ex);
            }
            return false;
        }
        public static bool AppendToXls(string fileName, List<List<string>> listData)
        {
            try
            {
                int maxCount = 500000;
                string fileNameWithoutExtension = System.IO.Path.GetFileNameWithoutExtension(fileName);
                if (listData.Count == 0)
                {
                    return false;
                }
                int fileCount = (listData.Count - 1) / maxCount + 1;
                if (fileCount == 1)
                {
                    var wk = NPOIOpenExcel(fileName);
                    ISheet sheet = wk.CreateSheet("Sheet1");
                    for (int i = 0; i < listData.Count; i++)
                    {
                        IRow row = sheet.CreateRow(i);
                        for (int j = 0; j < listData[i].Count; j++)
                        {
                            row.CreateCell(j).SetCellType(CellType.String);
                            row.CreateCell(j).SetCellValue(listData[i][j]);
                        }
                    }
                    if (File.Exists(fileName))
                    {
                        File.Delete(fileName);
                    }


                    using (FileStream fs = File.OpenWrite(fileName))
                    {
                        wk.Write(fs);
                    }
                }
                else
                {
                    FileInfo file = new FileInfo(fileName);
                    for (int k = 0; k < fileCount; k++)
                    {
                        string fileNameTmp = $"{file.Directory}\\{fileNameWithoutExtension}_part_{k + 1}{file.Extension}";
                        var wk = NPOIOpenExcel(fileNameTmp);
                        ISheet sheet = wk.CreateSheet("Sheet1");
                        for (int index = 0; index < maxCount; index++)
                        {
                            int dataIndex = k * maxCount + index;
                            if (dataIndex < listData.Count)
                            {
                                IRow row = sheet.CreateRow(index);
                                for (int j = 0; j < listData[dataIndex].Count; j++)
                                {
                                    row.CreateCell(j).SetCellValue(listData[dataIndex][j]);
                                }
                            }
                        }
                        if (File.Exists(fileNameTmp))
                        {
                            File.Delete(fileNameTmp);
                        }
                        using (FileStream fs = File.OpenWrite(fileNameTmp))
                        {
                            wk.Write(fs);
                        }
                    }
                }



                return true;
            }
            catch (Exception ex)
            {
                Log.E("导出excel 失败", ex);
                return false;
            }
        }

        public static bool AppendToTXT(string fileName, List<List<string>> listData)
        {
            try
            {
                lock (lockRW)
                {
                    StringBuilder content = new StringBuilder();
                    for (int i = 0; i < listData.Count; i++)
                    {
                        string strWrite = string.Join("\t", listData[i]);
                        content.AppendLine(strWrite);
                    }

                    using (StreamWriter file = new StreamWriter(fileName, true, Encoding.UTF8, 65536))
                    {
                        file.Write(content.ToString());
                    }
                }
                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            return false;
        }

        public static bool GetData(string fileName, out List<List<string>> listData, string sheetName = null)
        {
            listData = new List<List<string>>();
            ISheet sheet = null;
            IWorkbook workbook = null;
            DataTable data = new DataTable();
            int startRow = 0;
            try
            {
                var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                if (fileName.ToLower().IndexOf(".xlsx") > 0) // 2007版本
                    workbook = new XSSFWorkbook(fs);
                else if (fileName.ToLower().IndexOf(".xls") > 0) // 2003版本
                    workbook = new HSSFWorkbook(fs);

                if (sheetName != null)
                {
                    sheet = workbook.GetSheet(sheetName);
                    if (sheet == null) //如果没有找到指定的sheetName对应的sheet，则尝试获取第一个sheet
                    {
                        sheet = workbook.GetSheetAt(0);
                    }
                }
                else
                {
                    sheet = workbook.GetSheetAt(0);
                }
                if (sheet != null)
                {
                    IRow firstRow = sheet.GetRow(0);
                    int cellCount = firstRow.LastCellNum; //一行最后一个cell的编号 即总的列数

                    startRow = sheet.FirstRowNum;

                    //最后一列的标号
                    int rowCount = sheet.LastRowNum;
                    for (int i = startRow; i <= rowCount; ++i)
                    {
                        IRow row = sheet.GetRow(i);
                        if (row == null) continue; //没有数据的行默认是null　　　　　　　

                        List<string> listTmp = new List<string>();
                        for (int j = row.FirstCellNum; j < cellCount; ++j)
                        {
                            if (row.GetCell(j) != null) //同理，没有数据的单元格都默认是null
                                listTmp.Add(row.GetCell(j).ToString());
                            else
                            {
                                listTmp.Add("");
                            }
                        }
                        listData.Add(listTmp);
                    }
                }

                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception: " + ex.Message);
                return false;
            }
        }

        /// <summary>
        /// 将DataTable数据导入到excel中
        /// </summary>
        /// <param name="data">要导入的数据</param>
        /// <param name="isColumnWritten">DataTable的列名是否要导入</param>
        /// <param name="sheetName">要导入的excel的sheet的名称</param>
        /// <returns>导入数据行数(包含列名那一行)</returns>
        public void DataTableToExcel(string fileName, DataTable data, string sheetName, bool isColumnWritten)
        {
            int i = 0;
            int j = 0;
            int count = 0;
            ISheet sheet = null;
            IWorkbook workbook = null;
            var fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
            if (fileName.ToLower().IndexOf(".xlsx") > 0) // 2007版本
                workbook = new XSSFWorkbook();
            else if (fileName.ToLower().IndexOf(".xls") > 0) // 2003版本
                workbook = new HSSFWorkbook();

            try
            {
                if (workbook != null)
                {
                    sheet = workbook.CreateSheet(sheetName);
                }
                else
                {
                    return;
                }

                if (isColumnWritten == true) //写入DataTable的列名
                {
                    IRow row = sheet.CreateRow(0);
                    for (j = 0; j < data.Columns.Count; ++j)
                    {
                        row.CreateCell(j).SetCellValue(data.Columns[j].ColumnName);
                    }
                    count = 1;
                }
                else
                {
                    count = 0;
                }

                for (i = 0; i < data.Rows.Count; ++i)
                {
                    IRow row = sheet.CreateRow(count);
                    for (j = 0; j < data.Columns.Count; ++j)
                    {
                        row.CreateCell(j).SetCellValue(data.Rows[i][j].ToString());
                    }
                    ++count;
                }
                workbook.Write(fs); //写入到excel
                return;
            }
            catch (Exception ex)
            {
            }
        }

        /// <summary>
        /// 将excel中的数据导入到DataTable中
        /// </summary>
        /// <param name="sheetName">excel工作薄sheet的名称</param>
        /// <param name="isFirstRowColumn">第一行是否是DataTable的列名</param>
        /// <returns>返回的DataTable</returns>
        public DataTable ExcelToDataTable(string fileName, bool isFirstRowColumn, string sheetName = null)
        {
            ISheet sheet = null;
            IWorkbook workbook = null;
            DataTable data = new DataTable();
            int startRow = 0;
            try
            {
                var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
                if (fileName.ToLower().IndexOf(".xlsx") > 0) // 2007版本
                    workbook = new XSSFWorkbook(fs);
                else if (fileName.ToLower().IndexOf(".xls") > 0) // 2003版本
                    workbook = new HSSFWorkbook(fs);

                if (sheetName != null)
                {
                    sheet = workbook.GetSheet(sheetName);
                    if (sheet == null) //如果没有找到指定的sheetName对应的sheet，则尝试获取第一个sheet
                    {
                        sheet = workbook.GetSheetAt(0);
                    }
                }
                else
                {
                    sheet = workbook.GetSheetAt(0);
                }
                if (sheet != null)
                {
                    IRow firstRow = sheet.GetRow(0);
                    int cellCount = firstRow.LastCellNum; //一行最后一个cell的编号 即总的列数

                    if (isFirstRowColumn)
                    {
                        for (int i = firstRow.FirstCellNum; i < cellCount; ++i)
                        {
                            ICell cell = firstRow.GetCell(i);
                            if (cell != null)
                            {
                                string cellValue = cell.StringCellValue;
                                if (cellValue != null)
                                {
                                    DataColumn column = new DataColumn(cellValue);
                                    data.Columns.Add(column);
                                }
                            }
                        }
                        startRow = sheet.FirstRowNum + 1;
                    }
                    else
                    {
                        startRow = sheet.FirstRowNum;
                    }

                    //最后一列的标号
                    int rowCount = sheet.LastRowNum;
                    for (int i = startRow; i <= rowCount; ++i)
                    {
                        IRow row = sheet.GetRow(i);
                        if (row == null) continue; //没有数据的行默认是null　　　　　　　

                        DataRow dataRow = data.NewRow();
                        for (int j = row.FirstCellNum; j < cellCount; ++j)
                        {
                            if (row.GetCell(j) != null) //同理，没有数据的单元格都默认是null
                                dataRow[j] = row.GetCell(j).ToString();
                        }
                        data.Rows.Add(dataRow);
                    }
                }

                return data;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception: " + ex.Message);
                return null;
            }
        }

        public ISheet GetSheet(string fileName, string sheetName = null)
        {
            ISheet sheet = null;
            IWorkbook workbook = null;

            var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
            if (fileName.ToLower().IndexOf(".xlsx") > 0) // 2007版本
                workbook = new XSSFWorkbook(fs);
            else if (fileName.ToLower().IndexOf(".xls") > 0) // 2003版本
                workbook = new HSSFWorkbook(fs);

            if (sheetName != null)
            {
                sheet = workbook.GetSheet(sheetName);
                if (sheet == null) //如果没有找到指定的sheetName对应的sheet，则尝试获取第一个sheet
                {
                    sheet = workbook.GetSheetAt(0);
                }
            }
            else
            {
                sheet = workbook.GetSheetAt(0);
            }
            return sheet;
        }
    }
}